﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections;
using System.Reflection;
using Microsoft.SharePoint;
using FTC.SharePoint.Evolution.Core;

namespace FTC.SharePoint.Evolution
{
    public enum SharePointVersion
    {
        SharePointServer2010,
        SharePointFoundation2010
    }
    public class SPEvolutionManager
    {
        private readonly SPEvolutionAssembly evoAssembly;
        private string Key
        {
            get { return evoAssembly.Key; }
        }

        private readonly SPProvider provider;

        public SPEvolutionManager(SharePointVersion sharepointVersion, Assembly asm, SPWeb web)
		{			
            if (asm == null)
                throw new ArgumentNullException("Assembly file not found");
            evoAssembly = new SPEvolutionAssembly(asm);
            provider = new SPProvider(web);
		}

        public void MakeEvolution()
        {
            MakeEvolution(-1);
        }
        public void MakeEvolution(long toVersion)
        {

            long targetVersion = toVersion < 0 ? this.evoAssembly.LastVersion : toVersion;

            IList<long> applied = provider.GetAppliedEvolutions(Key);
            IList<long> available = this.evoAssembly.EvolutionsTypes.Select(mInfo => mInfo.Version).ToList();

            SPEvolutionPlan plan = BuildMigrationPlan(targetVersion, applied, available);

            long currentDatabaseVersion = plan.StartVersion;
            //MigratorLogManager.Log.Started(currentDatabaseVersion, targetVersion);
            SPEvolutionLog.Instance.Write(String.Format("Applied {0} Evosteps, available {1} evosteps", applied.Count, available.Count));
            SPEvolutionLog.Instance.Write(String.Format("Evolution Start From Current={0} to Target={1}", currentDatabaseVersion, targetVersion));

            foreach (long currentExecutedVersion in plan)
            {
                ExecuteStep(currentExecutedVersion, currentDatabaseVersion);
                currentDatabaseVersion = currentExecutedVersion;
            }
        }
        public void ExecuteStep(long targetVersion, long currentDatabaseVersion)
        {
            IEvolution migration = this.evoAssembly.InstantiateMigration(targetVersion, this.provider);
            if (migration == null)
                throw new ArgumentNullException(String.Format("Evolution step with version {0} not found"));
            //Require.IsNotNull(migration, "Не найдена миграция версии {0}", targetVersion);

            try
            {
               // this.provider.BeginTransaction();

                if (targetVersion <= currentDatabaseVersion)

                {
                    SPEvolutionLog.Instance.Write(String.Format("Evolution Down Target={0} Name={1}", targetVersion, migration.Name));
                    migration.Down();
                    this.provider.EvolutionsUnApplied(targetVersion, Key);
                }
                else
                {
                    SPEvolutionLog.Instance.Write(String.Format("Evolution Up Target={0} Name={1}", targetVersion, migration.Name));                 
                    migration.Up();
                    this.provider.EvolutionsApplied(targetVersion, Key);
                }

               // this.provider.Commit();
            }
            catch (Exception ex)
            {
               // MigratorLogManager.Log.Exception(targetVersion, migration.Name, ex);
                SPEvolutionLog.Instance.Write(ex.Message, SPEvolutionLogLevel.Exception);
                // при ошибке откатываем изменения
               // this.provider.Rollback();
               // MigratorLogManager.Log.RollingBack(currentDatabaseVersion);
                throw;
            }
        }

        public static SPEvolutionPlan BuildMigrationPlan(long target, IEnumerable<long> applied, IEnumerable<long> availableEvos)
        {
            long startVersion = applied.Count() < 1 ? 0 : applied.Max();
            HashSet<long> set = new HashSet<long>(applied);

            // проверки
            var list = availableEvos.Where(x => x < startVersion && !set.Contains(x));
            if (list.Count() > 1)
            {
                throw new ArgumentException
                    ("in the web exist not applied evolutions step, but current schema version is newer");//, list);
            }

            set.UnionWith(availableEvos);

            var versions = target < startVersion
                            ? set.Where(n => n <= startVersion && n > target).OrderByDescending(x => x).ToList()
                            : set.Where(n => n > startVersion && n <= target).OrderBy(x => x).ToList();

            return new SPEvolutionPlan(versions, startVersion);
        }

        public void EnableSharePointLogs()
        {         
            SPEvolutionLog.Instance.LogTypes.Add(SPEvolutionLogType.SharePointLog);
        }

        public void EnableConsoleLogs()
        {         
            SPEvolutionLog.Instance.LogTypes.Add(SPEvolutionLogType.Console);
        }
    }
   
    //public class EvolutionsManager
    //{
    //    protected List<BaseEvolution> Evolutions { get; set; }


    //    protected EvolutionsManager() { }

    //    private sealed class SingletonCreator
    //    {
    //        private static readonly EvolutionsManager instance = new EvolutionsManager();
    //        public static EvolutionsManager Instance { get { return instance; } }
    //    }

    //    public static EvolutionsManager Instance
    //    {
    //        get { return SingletonCreator.Instance; }
    //    }

    //    public void MakeEvolution(string nameSpace, string weburl)
    //    {
    //        MakeEvolution(nameSpace, weburl, new Version(0, 0, 0, 0), null);
    //    }

    //    public void MakeEvolution(string nameSpace, string weburl, Version fromVersion, Version toVersion)
    //    {
           
    //        // do linq query 
    //        IEnumerable results = GetAllClasses(nameSpace, typeof(BaseEvolution).Name);
    //        Evolutions = new List<BaseEvolution>();
    //        if (results != null)
    //        {
    //            foreach (Type b in results)
    //            {
    //                object o = b.GetConstructor(new Type[0]).Invoke(null);

    //                //Console.WriteLine(.Count());
    //                System.Version v = ((EvoVersionAttribute)b.GetCustomAttributes(typeof(EvoVersionAttribute), true)[0]).Version;
    //                b.GetProperty("EvoVersion").SetValue(o, v, null);
    //                b.GetProperty("CurrentWebUrl").SetValue(o, weburl, null);

    //                Evolutions.Add((BaseEvolution)o);

    //            }
    //            //if (fromVersion == null)
    //            //     fromVersion = "1.0.0.0";
    //            // Version fVersion = new Version(fromVersion);
    //            if (toVersion != null)
    //            {
    //                //  Version tVersion = new Version(toVersion);
    //                List<BaseEvolution> res = (from a in Evolutions
    //                                       where a.EvoVersion >= fromVersion && a.EvoVersion <= toVersion
    //                                       select a).ToList();
    //                res.ForEach(x => x.Up());
    //            }
    //            else
    //            {
    //                Evolutions.ForEach(x => x.Up());
    //            }
    //        }
    //    }

    //    public static IEnumerable GetAllClasses(string nameSpace, string baseClassName)
    //    {
    //        try
    //        {
    //            //System.Security.Policy.Evidence evidence = new System.Security.Policy.Evidence(null, null);
    //            Assembly asm1 = Assembly.LoadWithPartialName(nameSpace);
    //            return asm1.GetTypes();
    //            //Assembly.Load(na
    //        }
    //        catch
    //        {
    //            throw new NotImplementedException("Error while load assembly");
    //        }
            
    //        //Assembly asm = Assembly.GetExecutingAssembly();
    //        //if (!String.IsNullOrEmpty(nameSpace))
    //        //    return asm.GetTypes()
    //        //        .Where(x => x.Namespace == nameSpace)
    //        //        .Where(x => x.BaseType.Name == baseClassName);            
    //        //else
    //        //    return asm.GetTypes();
    //    }

    //}
}
